今天就來將昨天學到的內容給實作起來吧! GOGO!
建立輸入輸出規格:
# 輸入
member_login_model = api.model("MembersLogin", {
"username": fields.String,
"password": fields.String
})
# 輸出
response = jsonify({
"message": "login successful",
"id": 0,
"ok": true
})
建立 Login API:
# member_controller.py
@member_ns.route("/login")
class MemberLoginAPI(Resource):
@member_ns.expect(member_login_model) # 根據 member_login_model 規定輸入格式
def post(self):
member = get_member_by_username(member_ns.payload["username"])
if not member:
return {"message": "User dose not exist"}, 401
if not check_password_hash(member.password_hash, member_ns.payload["password"] + member.salt):
return {"message": "Incorrect password"}, 401
member_data = {"username": member.username, "id": member.id}
access_token = create_access_token(identity=member, additional_claims=member_data) # 建立 JWT 中的 Private Claims
response = jsonify({
"message": "login successful",
"id": member.id,
"ok": True,
})
set_access_cookies(response, access_token) # 將 access_token、csrf_access_token 設置到 cookies 中
return response
# member_model.py
def get_member_by_username(username):
return Member.query.filter_by(username=username).first()
建立輸入輸出規格:
利用 API 文件建立輸入輸出。
# 輸入
@member_ns.route("/<int:id>/tasks")
# 輸出
task_model = api.model("Task", {
"id": fields.Integer(required=False),
"member_id": fields.Integer,
"title": fields.String(required=True),
"priority": fields.String(required=False),
"state": fields.String(required=False),
"start": fields.DateTime(required=False),
"deadline": fields.DateTime(required=False),
"description": fields.String(required=False)
})
Member Tasks API 邏輯流程:
# member_controller.py
@member_ns.route("/<int:id>/tasks")
class Protected(Resource):
@jwt_required()
@member_ns.marshal_with(task_model, as_list=True)
def get(self, id):
"Member Tasks"
member = get_member_by_id(id)
if not member: abort(400, "Member not found")
jwt_member_id = get_jwt()["id"]
if not id == jwt_member_id: abort(403, "Forbidden")
memberTasks = get_member_tasks(member)
return memberTasks
# member_model.py
def get_member_by_id(id):
return Member.query.filter_by(id=id).first()
def get_member_tasks(member):
return member.tasks
# models.py
class Member(db.Model):
tasks = db.relationship("Task", back_populates="member", cascade="all, delete-orphan")
class Task(db.Model):
member = db.relationship("Member", back_populates="tasks")
測試 API:
根據 Flask-JWT-Extended 文件推薦方式:
# app > __init__.py
@app.after_request
def refresh_expiring_jwts(response):
try:
exp_timestamp = get_jwt()["exp"]
now = datetime.now(timezone.utc)
target_timestamp = datetime.timestamp(now + timedelta(minutes=30))
if target_timestamp > exp_timestamp:
access_token = create_access_token(identity=get_jwt_identity())
set_access_cookies(response, access_token)
return response
except (RuntimeError, KeyError):
# Case where there is not a valid JWT. Just return the original response
return response